import InstanceMap from "../event/InstanceRemoveEvent";
import HotClass from "../hotconfig/HotClass";
import { hotConfig } from "../hotconfig/hotconfig";
import Log4R from "../log/Log4R";

const loginfo = Log4R.getLog('info');
/** 服务表 */
const services: Ctn.Services = {

}
/** 服务集合 */
const serviceList = new Array<Ctn.BaseService>();
/** 服务容器 */
export const container: Ctn.Container = {
    runService: game.GetService('RunService'),
    replicatedStorage: game.GetService('ReplicatedStorage'),
    players: game.GetService('Players'),

    /** 容器状态 */
    state: 'none',
    getService<T extends keyof Ctn.Services>(className: T): Ctn.Services[T] {
        while (container.state === 'none') {
            loginfo?.info('container not ready:', container.state)
            task.wait(1)
        }
        if (services[className] === undefined) {
            print("services", services)
            throw (`"${className}" is not init yet!`);
        }
        return services[className];
    },
    register() {

        serviceList.forEach(c => {
            c.register();
        });
        this.state = 'reg'
    },
    activeServices: function (): void {
        serviceList.forEach(c => {
            c.hot();
        });
        serviceList.forEach(c => {
            c.init();
        });
        this.state = 'init'
        serviceList.forEach(c => {
            c.run();
        });
        this.state = 'active'
    }
}
/** 远程事件 */
let playerActEvent: RemoteEvent | undefined;
/** 远程方法 */
let playerActFunction: RemoteFunction | undefined
const _NAME_PLAYERACTEVENT = "PlayerActEvent";
const _NAME_PLAYERACTFUNCTION = "PlayerActFunction";
//初始化远程对象
if (container.runService.IsServer()) {
    playerActEvent = new Instance('RemoteEvent');
    playerActEvent.Name = _NAME_PLAYERACTEVENT;
    playerActEvent.Parent = container.replicatedStorage;
    playerActFunction = new Instance('RemoteFunction');
    playerActFunction.Name = _NAME_PLAYERACTFUNCTION;
    playerActFunction.Parent = container.replicatedStorage;
} else {
    const initRemoteEvent = () => {
        playerActEvent = container.replicatedStorage.WaitForChild(_NAME_PLAYERACTEVENT) as RemoteEvent;
    }
    const initRemoteFunction = () => {
        playerActFunction = container.replicatedStorage.WaitForChild(_NAME_PLAYERACTFUNCTION) as RemoteFunction
    }
    while (!playerActEvent) {
        task.wait(1);
        initRemoteEvent();
    }
    while (!playerActFunction) {
        task.wait(1)
        initRemoteFunction();
    }

}
/** 用户session */
const sessionMap = new Map<Player, Ctn.Session>()
//初始化远程事件
if (container.runService.IsServer()) {
    playerActEvent.OnServerEvent.Connect((player, ...args) => {
        // loginfo?.info("OnServerEvent", player, args[0])
        const remoteParams = args[0] as {
            act: string,
            service: keyof Ctn.Services,
            entity: any
        }
        /** 容器中的服务 */
        const service = container.getService(remoteParams.service) as unknown as {
            [key: string]: ((service: any, entity: any, session?: Ctn.Session) => void)
        };
        const serverCalls = service['serverCalls'] as unknown as string[]
        if (!serverCalls || !serverCalls.find(c => c === remoteParams.act)) {
            return
        }
        return service[remoteParams.act](service, remoteParams.entity, sessionMap.get(player))
    })
    playerActFunction.OnServerInvoke = (player, ...args) => {
        // loginfo?.info("OnServerEvent", player, args[0])
        const remoteParams = args[0] as {
            act: string,
            service: keyof Ctn.Services,
            entity: any
        }
        /** 容器中的服务 */
        const service = container.getService(remoteParams.service) as unknown as {
            [key: string]: ((service: any, entity: any, session?: Ctn.Session) => void)
        };
        const serverCalls = service['serverCalls'] as unknown as string[]
        if (!serverCalls || !serverCalls.find(c => c === remoteParams.act)) {
            return
        }
        return service[remoteParams.act](service, remoteParams.entity, sessionMap.get(player))
    }
    game.GetService('Players').GetPlayers().forEach(c => {

    })
    container.players.PlayerAdded.Connect(player => {

        sessionMap.set(player, {
            player
        })
        // loginfo?.info('add new user', sessionMap.get(player))
    })
    container.players.PlayerRemoving.Connect((player) => {
        sessionMap.delete(player)
        // loginfo?.info('remove user', sessionMap.get(player))
    })
} else {
    playerActEvent.OnClientEvent.Connect(args => {
        // loginfo?.info("OnClientEvent", args)
        const remoteParams = args as {
            act: string,
            service: keyof Ctn.Services,
            entity: any
        }
        /** 容器中的服务 */
        const service = container.getService(remoteParams.service) as unknown as {
            [key: string]: (service: any, entity: any) => void
        };
        return service[remoteParams.act](service, remoteParams.entity)
    })
}

const characterMap = new InstanceMap<Model, Ctn.character.CharacterEntity>()
/**
 * 
 */
export class ContainerService<S, E> implements Ctn.Service<S, E> {

    public getInstanceMap() {
        return characterMap.instanceMap
    }
    protected setCharacter(characterEntiy: Ctn.character.CharacterEntity) {
        if (!characterEntiy.character) throw ('no character')

        if (!characterMap.has(characterEntiy.character)) {
            characterMap.set(characterEntiy.character, characterEntiy)
        }
    }
    protected getCharacter(character: Model) {
        return characterMap.get(character)
    }
    protected getConfig<T extends keyof CreatableInstances>(name: string, className: T, call?: () => void) {
        if (this.serviceName) {
            const p = game.GetService('ReplicatedStorage').WaitForChild('modules').WaitForChild(this.serviceName);
            let r = p.WaitForChild(name);
            while (!r.IsA(className)) {
                print('wait for ' + name)
                task.wait(1)
                r = p.WaitForChild(name);
            }
            // loginfo?.info("<----------getConfig---------->", r)
            task.spawn(() => {
                if (r.IsA('NumberValue')) {
                    r.Changed.Connect(() => {
                        if (call) {
                            call()
                        }
                    })
                }
                if (r.IsA('Vector3Value')) {
                    r.Changed.Connect(() => {
                        if (call) {
                            call()
                        }
                    })
                }
                if (r.IsA('StringValue')) {
                    r.Changed.Connect(() => {
                        if (call) {
                            call()
                        }
                    })
                }
                if (call) {
                    call()

                }
            })
            return r
        }
        throw ('cant find ' + name + ' as ' + className)
    }
    protected getCache<T extends keyof CreatableInstances>(name: string, className: T) {
        if (this.serviceName) {
            const r = game.GetService('StarterPlayer').WaitForChild('StarterPlayerScripts').WaitForChild(this.serviceName).WaitForChild(name);
            if (r.IsA(className)) {
                return r
            }
        }
        throw ('cant find ' + name + ' as ' + className)
    }
    private static instance: unknown;
    /**创建服务实例 */
    public static getInstance<T>() {
        const i = this.instance
        if (!i) {
            this.instance = new this();
        }
        return this.instance as T
    }
    /** 初始化服务:各个服务已经在容器中注册->服务各个事件绑定->初始化服务->启动服务 */
    public init() {

    }
    /** 启动服务:各个服务已经在容器中注册->服务各个事件绑定->初始化服务->启动服务 */
    public run() {

    }
    protected scanHot(s: LuaSourceContainer) {
        this.scan(s.Parent?.Parent?.FindFirstChild('hot'))
    }
    /** 获取文件夹 */
    protected scan(foler?: Instance) {

        if (foler) {
            hotConfig(foler, (c) => {

                if (c.IsA('ModuleScript')) {
                    const hotModule = require(c) as { default: { getInstance(): {} } };
                    if (!hotModule) return
                    let name = c.Name;
                    const firstChar = name.sub(0, 1);
                    name = string.lower(firstChar) + name.sub(2);
                    const newInstance = hotModule.default.getInstance();
                    (this as any)[name] = newInstance;
                    print("---------->new Instance", this)
                }
            })
        }
    }
    public hot() {

    }
    $c = container
    /** 注册serviceName */
    protected serviceName?: keyof Ctn.Services
    /** 可远程调用的服务器方法 */
    protected serverCalls?: (keyof this)[]
    /** 在容器中注册服务 */
    register() {
        if (!this.serviceName) return
        if (services[this.serviceName] !== undefined) {
            throw (this.serviceName + " is Duplicate!")
        }
        (services[this.serviceName] as any) = this
    }

    protected constructor() {
        /** 添加到服务集合 */
        serviceList.push(this)
    }
    /** 在客户端运行 */
    protected runInLocal() {
        return container.runService.IsClient();
    }
    /** 在开发模式studio运行 */
    protected runInStudio() {
        return container.runService.IsStudio();
    }
    /** 获取当前摄像机 */
    protected getCurrentCarema() {
        return game.Workspace.CurrentCamera
    }
    /**
     * 客户端服务端远程通讯
     * @param act 
     * @param entity 
     * @param session 
     */
    protected fire<T>(act: keyof S, entity: T, session?: Ctn.Session) {
        if (this.runInLocal()) {
            playerActEvent?.FireServer({
                service: this.serviceName,
                act, entity
            })
        } else if (session?.player) {
            playerActEvent?.FireClient(session.player, {
                service: this.serviceName,
                act, entity
            })
        } else {
            playerActEvent?.FireAllClients({
                service: this.serviceName,
                act, entity
            })
        }
    }
    /**
     * 客户端调用服务端等待返回值
     * @param act 
     * @param entity 
     * @returns 
     */
    protected invoke<T>(act: keyof this, entity: T) {
        if (this.runInLocal()) {
            return playerActFunction?.InvokeServer({
                service: this.serviceName,
                act, entity
            })
        }
    }
    protected getPlayerGui() {
        return game.GetService('Players').LocalPlayer.FindFirstChild('PlayerGui') as PlayerGui

    }
    /** 获取移动端GUI */
    protected getTouchGui() {
        return this.getPlayerGui().FindFirstChild('TouchGui')
    }
}